package com.dhms.uikit.media

import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.Uri
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.widget.FrameLayout
import android.widget.Toast
import androidx.core.net.toFile
import androidx.fragment.app.FragmentActivity
import coil.load
import com.dhms.uikit.AudioRecordBottomSheet
import com.dhms.uikit.R
import com.dhms.uikit.databinding.MediaThumbnailViewBinding
import com.dhms.uikit.databinding.MediaViewBinding
import com.dhms.uikit.isLocalResource
import com.dhms.uikit.media.MediaActivity.Companion.BROADCAST_FILE_PATH_KEY
import com.dhms.uikit.media.MediaActivity.Companion.BROADCAST_FILE_THUMBNAIL_KEY
import com.dhms.uikit.media.MediaActivity.Companion.BROADCAST_FILE_TYPE_KEY
import java.io.File
import java.util.*

class MediaView : FrameLayout {

    private lateinit var binding: MediaViewBinding

    private val _mediaSet: MutableList<MediaItem> = mutableListOf()
    val mediaSet get() = _mediaSet

    private var viewId = UUID.randomUUID().toString()

    private lateinit var parentActivity: FragmentActivity

    private lateinit var audioRecordBottomSheet: AudioRecordBottomSheet

    private var onMediaAddedListener: ((media: MediaItem, viewId: String) -> Unit) = { _, _ -> }
    private var onMediaRemovedListener: ((media: MediaItem, viewId: String) -> Unit) = { _, _ -> }

    private var _isMediasPreview = false
    val isMediasPreview get() = _isMediasPreview

    constructor(context: Context) : super(context) {
        init(context, null)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
        context,
        attrs,
        defStyle
    )

    private fun init(context: Context, attrs: AttributeSet? = null) {
        binding = MediaViewBinding.inflate(LayoutInflater.from(context))

        if (attrs != null) {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.TitleCardView)
            _isMediasPreview =
                typedArray.getBoolean(R.styleable.MediaView_is_medias_preview, false)
            typedArray.recycle()
        }
        // Hide the entrance to create medias.
        if (isMediasPreview) {
            binding.mediaStartCamera.visibility = View.GONE
            binding.mediaStartAudio.visibility = View.GONE
        }

        addView(binding.root)
    }

    /**
     * Sets whether the media view supports editing or not.
     */
    fun setIsMediasPreview(isMediaPreview: Boolean) {
        _isMediasPreview = isMediaPreview
        if (isMediaPreview) {
            binding.mediaStartCamera.visibility = View.GONE
            binding.mediaStartAudio.visibility = View.GONE
        } else {
            binding.mediaStartCamera.visibility = View.VISIBLE
            binding.mediaStartAudio.visibility = View.VISIBLE
        }
    }

    /**
     * Sets the callback which would be called on a media added.
     */
    fun setOnMediaAddedListener(onMediaAddedListener: (media: MediaItem, viewId: String) -> Unit) {
        this.onMediaAddedListener = onMediaAddedListener
    }

    /**
     * Sets the callback which would be called on a media removed.
     */
    fun setOnMediaRemovedListener(onMediaRemovedListener: (media: MediaItem, viewId: String) -> Unit) {
        this.onMediaRemovedListener = onMediaRemovedListener
    }

    /**
     * Sets the parent activity of the view.
     */
    fun setParentActivity(activity: FragmentActivity) {
        parentActivity = activity
    }

    /**
     * Sets the view id of the media view which is used to receive broadcast and save files.
     */
    fun setViewId(viewId: String) {
        this.viewId = viewId
        setMediaBroadcastReceiver()
    }

    private val creatingBroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val fileUri = intent?.extras?.getParcelable<Uri>(BROADCAST_FILE_PATH_KEY)
            val fileType = intent?.extras?.getSerializable(BROADCAST_FILE_TYPE_KEY)
            val fileThumbnailUri = intent?.extras?.getParcelable<Uri>(BROADCAST_FILE_THUMBNAIL_KEY)
            if (fileUri != null) {
                addMedia(MediaItem(fileType as MediaType, fileUri, fileThumbnailUri))
            }
        }
    }

    private val deletingBroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            val fileUri = intent?.extras?.getParcelable<Uri>(BROADCAST_FILE_PATH_KEY)
            val fileType = intent?.extras?.getSerializable(BROADCAST_FILE_TYPE_KEY)
            val fileThumbnailUri = intent?.extras?.getParcelable<Uri>(BROADCAST_FILE_THUMBNAIL_KEY)
            if (fileUri != null) {
                removeMedia(MediaItem(fileType as MediaType, fileUri, fileThumbnailUri))
            }
        }
    }

    private fun setMediaBroadcastReceiver() {
        val creatingIntentFilter = IntentFilter(MediaActivity.BROADCAST_CREATE_ACTION + viewId)
        val deletingIntentFilter = IntentFilter(MediaActivity.BROADCAST_DELETE_ACTION + viewId)
        parentActivity.registerReceiver(creatingBroadcastReceiver, creatingIntentFilter)
        parentActivity.registerReceiver(deletingBroadcastReceiver, deletingIntentFilter)
    }

    /**
     * Sets the callback when the adding image medias button is clicked.
     */
    fun setOnCameraClickListener() {
        binding.mediaStartCamera.setOnClickListener {
            MediaActivity.startActivity(context, parentActivity, viewId)
        }
    }

    /**
     * Sets the callback when the adding audio medias button is clicked.
     */
    @SuppressLint("ClickableViewAccessibility")
    fun setOnAudioClickListener() {
        binding.mediaStartAudio.setOnTouchListener { _, p1 ->
            if (p1?.action == MotionEvent.ACTION_DOWN) {
                parentActivity.let {
                    audioRecordBottomSheet = AudioRecordBottomSheet.newInstance(viewId, false)
                    audioRecordBottomSheet.show(
                        it.supportFragmentManager,
                        AudioRecordBottomSheet.TAG
                    )
                }
            } else if (p1?.action != MotionEvent.ACTION_MOVE) {
                parentActivity.let {
                    it.supportFragmentManager.let {
                        audioRecordBottomSheet.dismiss()
                    }
                }
            }
            true
        }
    }

    /**
     * Add a media to the media view.
     */
    fun addMedia(media: MediaItem) {
        if (_mediaSet.size == MAX_MEDIA_COUNT) {
            throw Exception("最多支持 $MAX_MEDIA_COUNT 个媒体文件！")
        }

        if (isLocalResource(media.file)) {
            onMediaAddedListener(media, viewId)
        }

        _mediaSet.add(0, media)

        val imageViewBinding = MediaThumbnailViewBinding.inflate(LayoutInflater.from(context))
        if (media.type == MediaType.AUDIO) {
            imageViewBinding.thumbnailImage.setImageResource(R.drawable.img_media_audio_placeholder)
        } else {
            var picture = media.thumbnail ?: media.file
            if (!media.isNetworkResource && picture.path?.startsWith("file://") == false) {
                picture = Uri.fromFile(File(picture.path!!))
            }
            imageViewBinding.thumbnailImage.load(picture) {
                placeholder(R.drawable.img_photo_placeholder)
                error(R.drawable.img_photo_placeholder)
            }
        }
        if (!isMediasPreview) {
            imageViewBinding.cancelOption = {
                removeMedia(media)
            }
        }

        imageViewBinding.playOption = {
            val index = mediaSet.indexOf(media)
            MediaActivity.startActivity(
                context,
                parentActivity,
                viewId,
                MediaActivity.Companion.IntentType.MANAGE,
                mediaSet.map {
                    var file = it.file.toString()
                    if (!media.isNetworkResource && !file.startsWith("file://")) {
                        file = "file://$file"
                    }
                    if (it.type == MediaType.VIDEO) {
                        var thumbnail = it.thumbnail?.toString()
                        if (!media.isNetworkResource && thumbnail != null && thumbnail.startsWith("file://")) {
                            thumbnail = "file://$thumbnail"
                        }
                        file = "$file|$thumbnail"   // append thumbnail after url path
                    }
                    file
                }.toTypedArray(),
                index,
                isMediasPreview
            )
        }
        imageViewBinding.notShowPlayIcon = media.type != MediaType.VIDEO

        binding.mediaWrapper.addView(imageViewBinding.root, 0)

        if (mediaSet.size == MAX_MEDIA_COUNT) {
            binding.mediaStartAudio.visibility = GONE
            binding.mediaStartCamera.visibility = GONE
        }
    }

    /**
     * Add medias to the media view.
     */
    fun addAllMedias(mediaList: List<MediaItem>, isMediaPreview: Boolean) {
        val totalSize = mediaSet.size + mediaList.size
        setIsMediasPreview(isMediaPreview)
        if (totalSize > MAX_MEDIA_COUNT) {
            mediaList.subList(0, MAX_MEDIA_COUNT - mediaSet.size).forEach {
                addMedia(it)
            }
            throw Exception("最多支持 $MAX_MEDIA_COUNT 个媒体文件！")
        } else {
            mediaList.forEach {
                addMedia(it)
            }
        }
    }

    private fun removeMedia(mediaItem: MediaItem) {
        try {
            if (isLocalResource(mediaItem.file)) {
                deleteSavedFile(mediaItem.file)
            }

            onMediaRemovedListener(mediaItem, viewId)

            binding.mediaWrapper.removeViewAt(mediaSet.indexOf(mediaItem))
            mediaSet.remove(mediaItem)

            if (mediaSet.size < MAX_MEDIA_COUNT) {
                binding.mediaStartAudio.visibility = VISIBLE
                binding.mediaStartCamera.visibility = VISIBLE
            }
        } catch (e: Exception) {
            Toast.makeText(context, "删除失败！", Toast.LENGTH_SHORT).show()
        }
    }

    /**
     * Add medias from the media view.
     */
    fun removeAllMedias() {
        binding.mediaWrapper.removeViews(0, mediaSet.size)
        binding.mediaStartAudio.visibility = VISIBLE
        binding.mediaStartCamera.visibility = VISIBLE
        mediaSet.forEach {
            if (isLocalResource(it.file)) {
                deleteSavedFile(it.file)
            }
        }
        _mediaSet.clear()
    }

    private fun deleteSavedFile(uri: Uri) {
        val file = uri.toFile()
        if (file.exists() && file.isFile) {
            if (file.delete()) {
                Log.d(TAG, "Success to delete the file $file.")
            } else {
                throw Exception("Failed to delete the file $file.")
            }
        } else {
            Log.d(TAG, "Try to delete the file $file, but it is not exist.")
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        parentActivity.unregisterReceiver(creatingBroadcastReceiver)
        parentActivity.unregisterReceiver(deletingBroadcastReceiver)
    }

    companion object {
        const val MAX_MEDIA_COUNT = 9
        const val TAG = "MediaView"
    }
}